﻿using Stardust.Common.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Stardust.Server
{
    class ActionManager
    {
        private static Type serviceType = typeof(IStardustService);

        public static Dictionary<string, ActionWrapper> actionDict;

        public static void LoadActions()
        {
            actionDict = new Dictionary<string, ActionWrapper>();
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (serviceType.IsAssignableFrom(type))
                    {
                        string bigPath = null;
                        var clsAttributes = type.GetCustomAttributes(StardustNameAttribute.ThisType, false);
                        var servicePathAttribute = clsAttributes.FirstOrDefault() as StardustNameAttribute;
                        if (servicePathAttribute != null)
                        {
                            bigPath = servicePathAttribute.Name;
                        }
                        else
                        {
                            if (type.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase))
                            {
                                bigPath = type.Name.Remove(type.Name.Length - 7, 7);
                            }
                            else
                            {
                                bigPath = type.Name;
                            }
                        }
                        var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                        foreach (var method in methods)
                        {
                            if (method.ReturnType == typeof(void)) continue; //必须具有返回值
                            var methodAttributes = method.GetCustomAttributes(StardustNameAttribute.ThisType, false);
                            var methodPathAttr = methodAttributes.FirstOrDefault() as StardustNameAttribute;
                            string smallPath = null;
                            if (methodPathAttr != null)
                            {
                                smallPath = methodPathAttr.Name;
                            }
                            else
                            {
                                smallPath = method.Name;
                            }
                            var actionWrapper = new ActionWrapper("/" + bigPath + "/" + smallPath, type, method);
                            actionDict[actionWrapper.Path.ToUpper()] = actionWrapper;
                        }
                    }
                }
            }
        }

        public static ActionWrapper GetActionWrapper(string path)
        {
            ActionWrapper wrapper = null;
            if (actionDict.TryGetValue(path.ToUpper(), out wrapper))
            {
                return wrapper;
            }
            else
            {
                return null;//或default
            }
        }
    }

    class ActionWrapper
    {
        ParameterInfo[] Parameters;
        bool _isAsync = false;
        public ActionWrapper(string path, Type type, MethodInfo methodInfo)
        {
            Parameters = methodInfo.GetParameters();
            Path = path;
            _createObjFun = DynamicMethodHelper.BuildConstructorInvoker(type.GetConstructor(Type.EmptyTypes));
            _action = DynamicMethodHelper.BuilderMethodInvoker(methodInfo);
            if (methodInfo.ReturnType.Name == "Task`1")
            {
                _isAsync = true;
                _actionTask = DynamicMethodHelper.BuildContinueTaskInvoker(methodInfo.ReturnType);
            }
        }

        Func<object, object[], object> _action;
        Func<object> _createObjFun;
        Func<object, Task<object>> _actionTask;


        public string Path { get; set; }

        public object Invoke(string json)
        {
            var obj = _createObjFun();
            var ps = FatchParams(json);
            var result = _action(obj, ps);
            if (_isAsync)
            {
                var task = _actionTask(result);
                task.Wait();//并不完美，用异步处理HTTP请求才完美
                return task.Result;
            }
            return result;
        }

        private object[] FatchParams(string json)
        {
            if (Parameters.Length == 0)
            {
                return new object[] { };
            }
            ////类
            if (Parameters.Length == 1 && !TypeHelper.IsSimpleType(Parameters[0].ParameterType))
            {
                var arg0 = SerializerHelper.DeserializeFromString(json, Parameters[0].ParameterType);
                return new object[] { arg0 };
            }

            //简单类型
            Dictionary<string, object> ignoreCaseDict = null;
            if (string.IsNullOrEmpty(json))
            {
                ignoreCaseDict = new Dictionary<string, object>();
            }
            else
            {
                var dict = SerializerHelper.DeserializeFromString(json, TypeHelper.Types.StringObjectDict) as Dictionary<string, object>;
                ignoreCaseDict = new Dictionary<string, object>(dict, StringComparer.OrdinalIgnoreCase);
            }
            var args = new object[Parameters.Length];

            for (int i = 0; i < Parameters.Length; i++)
            {
                var p = Parameters[i];
                object value;
                ignoreCaseDict.TryGetValue(p.Name, out value);
                if (value == null)
                {
                    value = p.DefaultValue is DBNull ? null : p.DefaultValue;
                }
                if (value != null)
                {
                    args[i] = Convert.ChangeType(value, p.ParameterType);
                }
                else
                {
                    args[i] = null;
                }
            }
            return args;
        }

    }
}
